/**
 *  EKology - An Ecosystem Simulation Framework
 *  Copyright (C) 2008  Aarón Tavío - aaron.tavio at gmail.com - ULPGC
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 * 
 */

package net.ekology.core;

import net.ekology.ekosystem.*;
import net.ekology.ekosystem.datatypes.*;
import net.ekology.core.datatypes.*;
import net.ekology.graphics.*;
import net.ekology.core.gui.EKologyViewport;
import net.ekology.core.gui.datatypes.EKosystemModel;

import java.awt.Graphics2D;
import java.util.Vector;
import java.io.*;


/**
 * A fecade between EKology's core and the GUI
 * 
 * @author Aarón Tavío - aaron.tavio at gmail.com
 * @version 1.0.0 - 20081019-1544
 */
public class Fecade {
    private EKConfig oConfig;
    private Monitor oMonitor;
    private AgentAutomatonManager oAAM;
    private EKosystem oEKosystem;
    private EKologyViewport oViewport;
    
    
    
    private Fecade() {
        oConfig = EKConfig.getInstance();
        oMonitor = null;
        oAAM = null;
        oEKosystem = null;
        oViewport = null;
    }

    
    private static class SingletonHolder { // Inner class
        private final static Fecade INSTANCE = new Fecade();
    }
 
    
    public static Fecade getInstance() { //
        return SingletonHolder.INSTANCE;
    }
    
    
    public void loadProject(InputStream isInput) {
        ObjectInputStream oisInput;
        
        oisInput = null;
        if (isInput instanceof ObjectInputStream) oisInput = (ObjectInputStream) isInput;

        try {
            if (oisInput == null) oisInput = new ObjectInputStream(isInput);

            oMonitor = (Monitor) oisInput.readObject();
            oEKosystem = (EKosystem) oisInput.readObject();
            oAAM = (AgentAutomatonManager) oisInput.readObject();
        } catch (Exception e) {e.printStackTrace();}
        
    }
    
    public void saveProject(OutputStream osOutput) {
        ObjectOutputStream oosOutput;
        
        oosOutput = null;
        if (osOutput instanceof ObjectOutputStream) oosOutput = (ObjectOutputStream) osOutput;
        
        try {
            if (oosOutput == null) oosOutput = new ObjectOutputStream(osOutput);
            
            oosOutput.writeObject(oMonitor);
            oosOutput.writeObject(oEKosystem);
            oosOutput.writeObject(oAAM);
        } catch(Exception e) {e.printStackTrace();}
        
    }
    
    
    public void setViewport(EKologyViewport oViewport) {
        this.oViewport = oViewport;
    }
    
    
    public EKologyViewport getViewport() {
        return this.oViewport;
    }
    
    
    public void repaintViewport() {
        oViewport.repaint();
    }
       
    
    public void drawEKosystem(Graphics2D g2) {
        if (oEKosystem != null) oEKosystem.draw(g2);
    }
    
    
    public Vector<Object> loadModel(File oModelDirectory) {
        Object oObj;
        Vector<Object> vModel;
        Biotope oBiotope;
        Class oClass;
        DatabaseManager oDBM;
        
        vModel = null;
        if (oModelDirectory.isDirectory()) {
            vModel = new Vector<Object>();
            
            vModel.addAll(EKosystemModel.getModelClasses(oModelDirectory));
            vModel.addAll(EKosystemModel.getModelGenomes(oModelDirectory));
            
            for(int i=0; i<vModel.size(); i++) {
                oObj = vModel.elementAt(i);
                if (oObj instanceof Class) {
                    oClass = (Class) oObj;
                    if (oClass.getSuperclass().getSimpleName().equals("Biotope")) {
                        vModel.remove(i);
                        try {vModel.add(0, oClass.newInstance());} catch (Exception ignore){}
                        break;
                    }
                } else break;

            }

            oBiotope = (vModel != null)? (Biotope) vModel.firstElement() : null;
            oEKosystem = new EKosystem(oBiotope);

            oDBM = new DatabaseManager(oConfig.getString("DDBB_SERVER"),oConfig.getString("DDBB_DATABASE"),oConfig.getString("DDBB_USER"),oConfig.getString("DDBB_PASSWORD"));
            oMonitor = new Monitor(oEKosystem, oDBM);
            oAAM = new AgentAutomatonManager(oEKosystem,oMonitor);
        }
        
        return vModel;
    }
    
    
    public void addAgent(Agent oAgent) {
        oAgent.setBiotope(oEKosystem.getBiotope());
        oAgent.initialize();
        oEKosystem.addAgent(oAgent);
    }
    
    
    public void removeAgent(Agent oAgent) {
        String sAgentID;
        
        sAgentID = oAgent.getID();
        oEKosystem.removeAgent(sAgentID);
    }
    
    
    public void runSimulation() {
        oAAM.playResume();
    }
    
    
    public void stepSimulation() {
        oAAM.step();
    }
    
    
    public void stopSimulation() {
        this.oAAM.pause();
    }
    
    public Agent getNearestAgent(EKVector oPosition) {
        Agent oNearestAgent;
        EKVector oDistance;
        double dMinDistance;
        
        oNearestAgent = null;
        dMinDistance = Double.MAX_VALUE;
        
        for(Agent oAgent: oEKosystem.getAgents()) {
            oDistance = oPosition.clone();
            oDistance.sub(oAgent.getPosition());
            if (oDistance.module()<dMinDistance || oNearestAgent == null) {
                oNearestAgent = oAgent;
                dMinDistance = oDistance.module();
            }
        }
        
        return oNearestAgent;
    }
    
    
    public Agent[] getAgents() {
        return oEKosystem.getAgents();
    }
    
    public ProjectData getProjectData() {
        return oMonitor.getProjectData();
    }
    
    public void setProjectData(ProjectData oProjectData) {
        oMonitor.setProjectData(oProjectData);
    }
    
    public SessionData getSessionData() {
        return oMonitor.getSessionData();
    }
    
    public void setSessionData(SessionData oSessionData) {
        oMonitor.setSessionData(oSessionData);
    }
    
    public Signal[] getSignals() {
        return oMonitor.getSignals();
    }
    
    public void setSignals(Signal[] aSignal) {
        oMonitor.setSignals(aSignal);
    }
    
    public boolean getMonitoring() {
        return oMonitor.getMonitoring();
    }
    
    public void setMonitoring(boolean bValue) {
        oMonitor.setMonitoring(bValue);
    }
    
    public void setDrawing(boolean bValue) {
        oAAM.setDrawing(bValue);
    }
    
    public void setTimeFactor(double dTimeFactor) {
        oEKosystem.setTimeFactor(dTimeFactor);
    }
    
}